﻿using System;
using System.IO;
/*using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
*/

namespace IupNET
{
    /// <summary>
    /// This image wraps the Image, ImageRGB and ImageRGBA classes
    /// </summary>
    public class Image:IupBase
    {
        //TODO: split iupcontrol so that we dont have events etc. in this class
        public Image(int width,int height,int bpp,byte[] pixels):base(IntPtr.Zero) {
            Create(width, height, bpp, pixels);
        }

        public Image(byte[] rawdata):base(IntPtr.Zero)
        {
            CreateFromMemoryBuffer(rawdata);
        }

        public Image(string filename)
            : base(IntPtr.Zero)
        {
            IntPtr img = Iupc.LoadImage(filename);
            if (img == IntPtr.Zero)
                throw new Exception("Failed to load image " + filename);
            Initialize(img);
        }

        public Image(System.Drawing.Bitmap bitmap):base(IntPtr.Zero)
        {
            //we dont want to use System.Drawing at all in this API but since
            //resource manager emits Bitmaps sometimes its nice to be able to load such.
            //we do this with an ugly stream hack.
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
            CreateFromStream(ms);
            ms.Dispose();
        }

        /// <summary>
        /// Creates an image from data in stream. Note that the stream must contain tha image only,
        /// and its data must start at position 0 in stream. Sequential loading of multiple images from a stream
        /// is a future subject.
        /// </summary>
        /// <param name="stream"></param>
        public Image(Stream stream):base(IntPtr.Zero)
        {
            CreateFromStream(stream);
        }

       
        private void CreateFromStream(Stream stream)
        {
            //we have an optimization here: if the stream is a memory stream we have the byte array
            //prepared already, otherwise we have to create it...
            MemoryStream ms = stream as MemoryStream;
            if (ms != null)
            {
                CreateFromMemoryBuffer(ms.GetBuffer());
            }
            else
            {
                stream.Position = 0;
                byte[] bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);
                CreateFromMemoryBuffer(bytes);
            }
        }
        
        private void CreateFromMemoryBuffer(byte[] rawdata)
        {
            BinFileModule oldmodule = Imc.BinFileSetCurrentModule(BinFileModule.MemFile);
            try
            {

                IntPtr memname = Imc.CreateMemFileName(ref rawdata[0], rawdata.Length);
                if (memname == IntPtr.Zero)
                    throw new Exception("Failed to create memory loader structure");

                Initialize(Iupc.LoadImageFromMemory(memname));

                Imc.DestroyMemeFileName(memname);
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                Imc.BinFileSetCurrentModule(oldmodule);
            }
        }


        private void Create(int width, int height, int bpp, byte[] pixels)
        {
            if (bpp == 8)
                Initialize(Iupc.Image(width, height, pixels));
            else if (bpp == 24)
                Initialize(Iupc.ImageRGB(width, height, pixels));
            else if (bpp == 32)
                Initialize(Iupc.ImageRGBA(width, height, pixels));
            else
                throw new Exception("Invalid bit depth for image, only 8,24 and 32 are supported");

        }

        /// <summary>
        /// For 8 bit images, this function sets entries in the palette.
        /// The indices can range from 0 to 255. The total number of colors is limited to 256 colors. 
        /// The values are integer numbers from 0 to 255. If the value of a given index is same as "BgColor", 
        /// the color used will be the background color of the element on which the image will be inserted. 
        /// The "BgColor" value must be defined within an index less than 16.
        /// </summary>
        public void SetPalette(int index, Color c)
        {
            if (index < 0 || index > 255)
                return;
            SetAttribute(index.ToString(), Convert.ColorToString(c));
        }

        /// <summary>
        /// For 8 bit images, this function gets entries in the palette.
        /// The indices can range from 0 to 255. The total number of colors is limited to 256 colors. 
        /// The values are integer numbers from 0 to 255. 
        /// </summary>
        public Color GetPalette(int index)
        {
            if (index < 0 || index > 255)
                return Color.Empty;
            string col = GetAttribute(index.ToString());
            return Convert.StringToColor(col);
        }


        /// <summary>
        /// Gets the number of bits per pixel in the image. 
        /// This value is 8, 24 or 32 .
        /// </summary>
        public int BPP
        {
            get
            {
                return Convert.StringToInt(GetAttribute("BPP"));
            }
        }


        /// <summary>
        /// Gets or sets the color used for transparency. 
        /// If not defined uses the BgColor of the control that contains the image.
        /// </summary>
        public Color BgColor
        {
            get
            {
                return Convert.StringToColor(GetAttribute("BGCOLOR"));
            }
            set
            {
                SetAttribute("BGCOLOR", Convert.ColorToString(value));
            }
        }

        /// <summary>
        /// Gets the number of channels in the image. 
        /// This value is 1,3 or 4 depending on how the image is created.
        /// </summary>
        public int Channels
        {
            get
            {
                return Convert.StringToInt(GetAttribute("CHANNELS"));
            }
        }

        /// <summary>
        /// Gets the image height in pixels.
        /// </summary>
        public int Height
        {
            get
            {
                return Convert.StringToInt(GetAttribute("HEIGHT"));
            }
        }

        /// <summary>
        /// Gets the image width in pixels.
        /// </summary>
        public int Width
        {
            get
            {
                return Convert.StringToInt(GetAttribute("WIDTH"));
            }
        }

        /// <summary>
        /// Gets or sets the Hotspot point, which is the position inside a cursor image indicating the mouse-click spot. 
        /// Its value is given by the x and y coordinates inside a cursor image. Default is 0,0 .
        /// </summary>
        public Point2i Hotspot
        {
            get
            {
                return Convert.StringToPoint(GetAttribute("HOTSPOT"));
            }

            set
            {
                SetAttribute("HOTSPOT", Convert.PointToString(value, ":"));
            }
        }

        /// <summary>
        /// Gets the image size in pixels.
        /// </summary>
        public Size2i RasterSize
        {
            get
            {
                return Convert.StringToSize(GetAttribute("RASTERSIZE"));
            }
        }

        /// <summary>
        /// Gets the internal pixels data pointer.
        /// </summary>
        public IntPtr WID
        {
            get
            {
                return Iupc.GetAttributePtr(cobject, "WID");
            }

        }
        
    }
}
